/*
 * @Author: tanjunyi@doctorwork.com
 * @Date: 2020-04-10 09:49:05
 * @LastEditors: tanjunyi@doctorwork.com
 * @LastEditTime: 2020-05-19 12:06:44
 * @Description:
 */

let systemFlag = 1;
/**
 * 收集所有回调函数，side-effects
 *
	{
		ObservableObject@1 : {
			num : ()=>{
				callback()
			},
		}
 	}
 *
 */
let observer = {};
/**
 * 临时存储副作用
 */
let observerTemp = {};
// ------------------ class相关 ---------------------------
// 代理对象
let descriptorCache = {};
// 用来存储被装饰器增强的属性对象，例如class中 @observable 后面的属性
var mobxPendingDecorators = Symbol("mobx pending decorators");
// 隐藏属性，标记类的原型对象是否被实例化过
var mobxDidRunLazyInitializersSymbol = Symbol("mobx did run lazy initializers");
// 代理实例对象隐藏属性，标记是否已经被实例化
var $mobx = Symbol("mobx administration");
/**
 * 将key转换成字符串
 * @param {*} key 
 */
function stringifyKey(key) {
	if (key && key.toString)
		return key.toString();
	else
		return new String(key).toString();
}
/**
 * 判断是否是原始对象
 * @param {*} value 
 */
function isPlainObject(value) {
    if (value === null || typeof value !== "object")
        return false;
    var proto = Object.getPrototypeOf(value);
    return proto === Object.prototype || proto === null;
}
/**
 * 添加隐藏属性
 * @param {*} object 
 * @param {*} propName 
 * @param {*} value 
 */
function addHiddenProp(object, propName, value) {
	Object.defineProperty(object, propName, {
		enumerable: false,
		writable: true,
		configurable: true,
		value: value
	});
}

/**
 * 初始化实例对象
 * @param {*} target 对象本身
 */
function initializeInstance(target) {
	if (target[mobxDidRunLazyInitializersSymbol]) return;
	// ---
	var decorator = target[mobxPendingDecorators];
	if (decorator) {
		//标记已经完成初始化
		addHiddenProp(target, mobxDidRunLazyInitializersSymbol, true);
		// 获取target和装饰器对象上所有的key
		var keys = Object.getOwnPropertySymbols(decorator).concat(Object.keys(decorator))
		keys.forEach(key => {
			let d = decorator[key]
			d.propertyCreator(target, d.prop, d.descriptor)
		})

	}
}
/**
 * 被观测对象添加属性
 */
var observablePropertyConfigs = Object.create(null);
function generateObservablePropConfig(propName) {
	return (observablePropertyConfigs[propName] ||
		(observablePropertyConfigs[propName] = {
			configurable: true,
			enumerable: true,
			get: function () {
				let ObserveFlag = this[$mobx].name
				CollectDependencies(propName,ObserveFlag)
				return this[$mobx].read(propName);
			},
			set: function (v) {
				let ObserveFlag = this[$mobx].name
				applyModifyEffect(propName,ObserveFlag)
				this[$mobx].write(propName, v);
			}
		}));
}
/**
 * 被观测对象
 * NOTO: 所有的依赖收集在这里
 */
var ObservableValue = /** @class */ (function () {

	class ObservableValue {
		constructor(name, value) {
			this.value = value;
			this.name = name;
		}
		get() {
			
			return this.value
		}
		set(newValue) {
			this.value = newValue
		}
	}
	ObservableValue.prototype.prepareNewValue = function (newValue) {
		return this.value;
	};
	ObservableValue.prototype.setNewValue = function (newValue) {
		var oldValue = this.value;
		this.value = newValue;
	};
	return ObservableValue;
})()
/**
 * 代理对象base
 */
var ObservableObjectAdministration = /** @class */ (function () {
	function ObservableObjectAdministration(target, values, name, defaultEnhancer) {
		if (values === void 0) { values = new Map(); }
		this.target = target;
		this.values = values;
		this.name = name;
		this.defaultEnhancer = defaultEnhancer;
		// this.keysAtom = new Atom(name + ".keys");
	}
	ObservableObjectAdministration.prototype.read = function (key) {
		return this.values.get(key).value;
	};
	ObservableObjectAdministration.prototype.write = function (key, newValue) {
		var observable = this.values.get(key);
		observable.set(newValue);
		newValue = observable.prepareNewValue(newValue);
		observable.setNewValue(newValue);
	};
	ObservableObjectAdministration.prototype.addObservableProp = function (propName, newValue) {
		var target = this.target;

		var observable = new ObservableValue(this.name + "." + stringifyKey(propName), newValue);
		this.values.set(propName, observable);
		newValue = observable.value; // observableValue might have changed it
		Object.defineProperty(target, propName, generateObservablePropConfig(propName));
		// this.notifyPropertyAddition(propName, newValue);
	};
	return ObservableObjectAdministration;
})()

/**
 * 创建实例的代理对象
 * @param {*} target 
 */
function asObservableObject(target, name, defaultEnhancer) {
	// 判断是否已经初始化过了
	if (Object.prototype.hasOwnProperty.call(target, $mobx)) {
		return target[$mobx];
	}
	if (!isPlainObject(target))
        name = (target.constructor.name || "ObservableObject") + "@" +   systemFlag++;
    if (!name)
        name = "ObservableObject@" +   systemFlag++;
	let adm = new ObservableObjectAdministration(target, new Map(), stringifyKey(name), defaultEnhancer)
	addHiddenProp(target, $mobx, adm);
	return adm;
}

/**
 * 创建装饰器增强
 * @param {*} target 
 * @param {*} prop 
 * @param {*} descriptor 
 */
function createDecoratorForEnhancer(target, prop, descriptor) {
	// 判断target上是否有装饰器增强对象
	if (!Object.prototype.hasOwnProperty.call(target, mobxPendingDecorators)) {
		var inheritedDecorators = target[mobxPendingDecorators];
		addHiddenProp(target, mobxPendingDecorators, Object.assign({}, inheritedDecorators));
	}
	// 在这里注入初始化回调函数
	function propertyCreator(target, propertyName, descriptor) {
		var initialValue = descriptor
			? descriptor.initializer
				? descriptor.initializer.call(target)
				: descriptor.value
			: undefined;
		// 生成新的实例对象，并且将新对象prop属性的访问进行代理转发。
		asObservableObject(target).addObservableProp(propertyName, initialValue);
	}
	// 将装饰器增强对象挂在到target上
	target[mobxPendingDecorators][prop] = {
		prop: prop,
		propertyCreator: propertyCreator,
		descriptor: descriptor,
	}
	// 返回代理对象
	return descriptorCache[prop] || (descriptorCache[prop] = {
		configurable: true,
		enumerable: false,
		get: function () {
			initializeInstance(this);
			return this[prop];
		},
		set: function (value) {
			initializeInstance(this);
			this[prop] = value;
		}
	})
}

/**
 * 当所有GET请求触发时判断收集依赖
 * @param {*} prop 
 */
function CollectDependencies(prop,ObserveFlag){
	if (isCollect && (typeof prop == 'string')) {
		// 临时存储所有的回调 ,例如： { key1 : callback1 }
		observerTemp[ObserveFlag] = {
			[prop]: currentCallback
		}
	}
	// 准备回调
	if (listenerFlag && (typeof prop == 'string')) {
		// 存储所用的keys ,例如：['key1','key2']
		if (listeners[ObserveFlag]) {
			listeners[ObserveFlag].add(prop)
		} else {
			listeners[ObserveFlag] = new Set([prop])
		}
	}
}
/**
 * 当执行set操作时判断如果非严格模式则执行所有的副作用
 * @param {*} target 
 * @param {*} prop 
 */
function applyModifyEffect(prop,ObserveFlag){
// 这里通过是否必须通过action触发更改控制
	if (!isCollect && (typeof prop == 'string')) {
		if (enforceActions) {
			throw Error("当前enforceActions 模式无法对数据进行修改")
		}
		if (observer[ObserveFlag] && observer[ObserveFlag][prop]) {
			let applys = observer[ObserveFlag][prop];
			applys.forEach(fn => {
				fn.apply(this);
			})
		}

	}
}


/**
 * 对象转发
 * @param {*} target 
 * @param {*} propertyName 
 * @param {*} descriptor 
 */
function createObjectForEnhancer(target){

	// 返回代理对象
	let proxy =  new Proxy(target,{
		get: function (target,prop, receiver) {
			CollectDependencies(prop,target[$mobx])
			return Reflect.get(target,prop, receiver);
		},
		set: function (target,prop,value, receiver,) {
			applyModifyEffect(prop,target[$mobx]);
			return Reflect.set(target,prop,value, receiver)
		}
	})
	Object.defineProperty(proxy,$mobx,{
		value : "ObservableObject@" +   systemFlag++,
		writable : false,
		enumerable: false
	})
	// proxy[$mobx] = "ObservableObject@" +   systemFlag++;
	return proxy;
}

// -------------------- class相关结束 -------------------------
function observable(target, prop, descriptor) {
	if (typeof prop == 'string') {
		// FIXME： 这里用工厂模式，每次生成新的数据
		return createDecoratorForEnhancer.apply(null, arguments)
	} else {
		return createObjectForEnhancer.apply(null,arguments)
	}
}
/**
 * 回调缓存
 */
let currentCallback;
// NOTE: 暂时不需要
let isFirst = true;
/**
 * 正在收集side-effects
 */
let isCollect = true;

// NOTE: 这里暂时没实现 
class Reaction {
	constructor(type, callback) {
		// callback.call(actions);
		this.callback = callback.bind(this);
	}
	// 这里初始化，收集依赖
	track(fn) {
		observer = {}; //这里是覆盖收集，所以需要清除所有的依赖
		beforCollect(this.callback);
		fn();
		endCollect();
	}
}

function beforCollect(callback) {
	observerTemp = {};
	isCollect = true;
	currentCallback = callback;
}
function endCollect() {
	isCollect = false;
	// 开始组装observer
	Object.keys(observerTemp).map(observe => {
		Object.keys(observerTemp[observe]).map(prop => {
			// 做容错初始化
			if (!observer[observe]) {
				observer[observe] = {}
			}
			// 注入回调
			if (observer[observe][prop]) {
				observer[observe][prop].push(observerTemp[observe][prop])
			} else {
				observer[observe][prop] = [observerTemp[observe][prop]]
			}
		})

	})
}
function autorun(callback) {
	beforCollect(callback);
	callback();
	endCollect();
}

/**
 * action 等动作时先将操作记录到更新队列，endAction阶段统一执行更新程序
 * 主要是将操作记录
 */
let listenerFlag = false;
/**
 * 记录所有的回调操作，通过 { key , function }的形式
 */
let listeners = {};
function startAction() {
	// 开始收集依赖
	listeners = {};
	listenerFlag = true;
	isCollect = true;
}
/**
 * 
listeners : {
	ObservableObject@1 : ['num','grad']
}
 */


function endAction() {
	// 执行更新队列
	Object.keys(listeners).forEach(observe => {

		listeners[observe].forEach(prop => {
			// eg. ['name','key']
			if (!observer[observe] || (!observer[observe][prop])) return;
			if (Array.isArray(observer[observe][prop])) {
				let applys = observer[observe][prop];
				applys.forEach(fn => {
					fn.apply(this);
				})
			}
		})
	})
	listenerFlag = false;
	isCollect = false;
}
function action(type, fn) {
	// 参数处理,兼容如果没有传类型
	if (typeof type == 'function') {
		fn = type;
	}
	// console.log("注册的action：", type)
	return function () {
		startAction(); //这里开始放入队列
		fn.apply(this);
		endAction(); //释放状态
	}
}

// TODO: 这里暂时只区分true和false
let enforceActions = false;

function configure(config) {
	enforceActions = config.enforceActions;
}
// ------------ 装饰器封装 -------------------



module.exports = {
	Reaction,
	autorun,
	action,
	configure,
	observable
};